#pragma once

#define SIGNAL_MAX 1200
#define SIGNAL_MIN 0
#define SIGNAL_INIT 0

#define FIRE_SIG_COUNT 5
#define NORESPONSE_COUNT 2
#define EASY_COUNT 5
#define RESTRAIN_COUNT 50

#define BASE_RESTING_POTENTIAL 150
#define POTENTIAL_THRESHOLD 290

typedef enum{
	NERVE_TYPE_LEFT_TO_RIGHT,
	NERVE_TYPE_RIGHT_TO_LEFT,
}NERVE_ARROW_TYPE;

typedef enum{
	NERVE_TYPE_INSIDE,
	NERVE_TYPE_AT_TOP_BOUND,
	NERVE_TYPE_AT_BOTTOM_BOUND,
}NERVE_POS_TYPE;

typedef enum{
	STATUS_CALM,
	STATUS_FIRE,
	STATUS_NORESPONSE,
	STATUS_RESTRAIN,
	STATUS_EASY,
}NERVE_STATUS;

class CActiveEPSP
{
public:
	CActiveEPSP(int base):
	  m_nActiveTime(0),
	  m_nBase(base),
	  m_next(NULL)
	{};
	~CActiveEPSP(){};

	int GetFastEPSP( void ) 
	{
		if( m_nActiveTime>2 && m_nActiveTime<=20 )
			return m_nBase;
		return 0;
	};
	int GetLongEPSP( void ) 
	{
		if( m_nActiveTime>2+20+100 && m_nActiveTime<=2+20+100+200 )
			return m_nBase/30;
		return 0;
	};
	void TimeUp( void ) {m_nActiveTime++;};
	bool IsFinished( void ) {return m_nActiveTime>2+20+100+200;};

	CActiveEPSP* m_next;
private:
	int m_nActiveTime;
	int m_nBase;
};

class CActiveEPSPList
{
public:
	CActiveEPSPList( void) :
		m_pFirstActiveEPSP(NULL),
		m_pCurrentActiveEPSP(NULL),
		m_pLastActiveEPSP(NULL)
	{};
	~CActiveEPSPList() 
	{
		CActiveEPSP* pcur = m_pFirstActiveEPSP, *pnext = m_pFirstActiveEPSP ? m_pFirstActiveEPSP->m_next : NULL;
		while( pcur )
		{
			delete pcur;
			if( !pnext )
				break;
			pcur = pnext;
			pnext = pcur->m_next;
		}
	};

	void AddTail( CActiveEPSP* pActiveEPSP )
	{
		if( m_pLastActiveEPSP )
		{
			m_pLastActiveEPSP->m_next = pActiveEPSP;
			m_pLastActiveEPSP = pActiveEPSP;
		}
		else
		{
			m_pLastActiveEPSP = pActiveEPSP;
			m_pFirstActiveEPSP = m_pLastActiveEPSP;
		}
	};

	void Detach( CActiveEPSP* pActiveEPSP )
	{
		if( pActiveEPSP == m_pFirstActiveEPSP )
		{
			m_pFirstActiveEPSP = pActiveEPSP->m_next;
			if( !m_pFirstActiveEPSP )
				m_pLastActiveEPSP = NULL;
		}
		else
		{
			CActiveEPSP* pcur = m_pFirstActiveEPSP, *pprev;
			while( pcur && pcur!=pActiveEPSP )
			{
				pprev = pcur;
				pcur=pcur->m_next;
			}
			if( pcur )
			{
				pprev->m_next = pActiveEPSP->m_next;
				pActiveEPSP->m_next = pcur->m_next;

				if( m_pLastActiveEPSP == pActiveEPSP )
					m_pLastActiveEPSP = pprev;
			}
		}
	};

	void SetToFirst( void )
	{
		m_pCurrentActiveEPSP = m_pFirstActiveEPSP;
	};

	CActiveEPSP* GetNext( void )
	{
		CActiveEPSP* ptmp = m_pCurrentActiveEPSP;
		if( !m_pCurrentActiveEPSP )
			return NULL;

		m_pCurrentActiveEPSP = m_pCurrentActiveEPSP->m_next;
		return ptmp;
	};

	void Clear( void )
	{
		m_pLastActiveEPSP = NULL;
		m_pFirstActiveEPSP = NULL;
		m_pCurrentActiveEPSP = NULL;
	};

	void TimeUp( void )
	{
		CActiveEPSP* pcur = m_pFirstActiveEPSP, *pprev = m_pFirstActiveEPSP;
		while( pcur )
		{
			pcur->TimeUp();
			if( pcur->IsFinished() )
			{
				if( pcur == m_pFirstActiveEPSP )
				{
					m_pFirstActiveEPSP = pcur->m_next;
					if( !m_pFirstActiveEPSP )
						m_pLastActiveEPSP = NULL;
					pprev = m_pFirstActiveEPSP;

					delete pcur;
					pcur = pprev;
				}
				else
				{
					pprev->m_next = pcur->m_next;
					if( m_pLastActiveEPSP == pcur )
						m_pLastActiveEPSP = pprev;

					delete pcur;
					pcur = pprev->m_next;
				}
			}
			else
			{
				pprev = pcur;
				pcur = pcur->m_next;
			}
		}
	};

private:
	CActiveEPSP* m_pFirstActiveEPSP;
	CActiveEPSP* m_pCurrentActiveEPSP;
	CActiveEPSP* m_pLastActiveEPSP;
};

class CNerveArray;

class CNerve
{
public:
	CNerve( NERVE_ARROW_TYPE a_tp, NERVE_POS_TYPE pos_tp, int pos_x, int pos_y ) :
	  	m_nLeftInputSignal(SIGNAL_INIT),
		m_nRightInputSignal(SIGNAL_INIT),
		m_nLeftOutputSignal(SIGNAL_INIT),
		m_nRightOutputSignal(SIGNAL_INIT),
		m_nMainInputSignal(SIGNAL_INIT),
		m_nMainOutputSignal(SIGNAL_INIT),
		m_nLeftInputSignal_Buf(SIGNAL_INIT),
		m_nRightInputSignal_Buf(SIGNAL_INIT),
		m_nMainInputSignal_Buf(SIGNAL_INIT),
		m_bSignalIsMinus(false),
		m_pLeftInputNerve(NULL),
		m_pRightInputNerve(NULL),
		m_pLeftOutputNerve(NULL),
		m_pRightOutputNerve(NULL),
		m_pMainInputNerve(NULL),
		m_pMainOutputNerve(NULL),
		m_arrow_type(a_tp),
		m_pos_type(pos_tp),
		m_pos_x(pos_x),
		m_pos_y(pos_y),
		m_status(STATUS_CALM),
		m_countdown(0),
		m_potential(0)
		{
		};

	virtual ~CNerve(void) {};

	int Save( unsigned char* pNerveMem );
	bool Load( unsigned char* pNerveMem, CNerveArray* pArray );

	int GetMemSize( void ) 
	{ 
		unsigned char tmp[200];
		return Save(tmp); 
	};
	
	void LeftInput( int nSignal ) {m_nLeftInputSignal_Buf = nSignal>=140?139:nSignal;};
	void RightInput( int nSignal ) {m_nRightInputSignal_Buf = nSignal>=140?139:nSignal;};
	void Input( int nSignal ) {m_nMainInputSignal_Buf = nSignal>=140?139:nSignal;};

	void ReadSignal( int& nLeftIn, int& nMainIn, int& nRightIn, int& nLeftOut, int& nMainOut, int& nRightOut ) {nLeftIn=m_nLeftInputSignal; nMainIn=m_nMainInputSignal; nRightIn=m_nRightInputSignal; nLeftOut=m_nLeftOutputSignal; nMainOut = m_nMainOutputSignal; nRightOut = m_nRightOutputSignal; };

	int GetOutput( void ) { return m_nMainOutputSignal;};
	int GetLeftOutput( void ) { return m_nLeftOutputSignal;};
	int GetRightOutput( void ) { return m_nRightOutputSignal;};

	int GetPotential( void ) {return m_potential; };
	int GetRestingPotential( void ) {return m_resting_potential; };

	virtual bool PrepareProcess( void );
	virtual bool ProcessSignal( void );

	//virtual void Inspire( void );
	//virtual void Punish( void );

	void GetPosition( int& x, int& y ) {x=m_pos_x; y=m_pos_y;};

	bool ConnectTo( CNerve* pNerve )
	{
		if( pNerve->ConnectIn(this) )
			m_pMainOutputNerve=pNerve;
		else
			return false;
		return true;
	};

	bool LO_ConnectToLeft( CNerve* pNerve ) 
	{
		if( pNerve->LeftConnectIn(this) )
			m_pLeftOutputNerve=pNerve;
		else
			return false;
		m_bLO_to_LI = true;
		return true;
	};
	bool LO_ConnectToRight( CNerve* pNerve ) 
	{
		if( pNerve->RightConnectIn(this) )
			m_pLeftOutputNerve=pNerve;
		else
			return false;
		m_bLO_to_LI = false;
		return true;
	};
	bool RO_ConnectToLeft( CNerve* pNerve ) 
	{
		if( pNerve->LeftConnectIn(this) )
			m_pRightOutputNerve=pNerve;
		else
			return false;
		m_bRO_to_LI = true;
		return true;
	};
	bool RO_ConnectToRight( CNerve* pNerve ) 
	{
		if( pNerve->RightConnectIn(this) )
			m_pRightOutputNerve=pNerve;
		else
			return false;
		m_bRO_to_LI = false;
		return true;
	};

	void DisconnectLeft( void ) 
	{
		if( m_bLO_to_LI )
			m_pLeftOutputNerve->CutLeftConnectIn();
		else
			m_pLeftOutputNerve->CutRightConnectIn();
	};

	void DisconnectRight( void ) 
	{
		if( m_bRO_to_LI )
			m_pRightOutputNerve->CutLeftConnectIn();
		else
			m_pRightOutputNerve->CutRightConnectIn();
	};

	bool LeftConnectIn( CNerve* pNerve ) { if(m_pLeftInputNerve) return false; m_pLeftInputNerve=pNerve; return true;};
	bool RightConnectIn( CNerve* pNerve ) { if(m_pRightInputNerve) return false; m_pRightInputNerve=pNerve;return true;};

	bool ConnectIn( CNerve* pNerve ) { if(m_pMainInputNerve) return false; m_pMainInputNerve=pNerve;return true;};

	void CutLeftConnectIn( void ) {m_pLeftInputNerve=NULL;};
	void CutRightConnectIn( void ) {m_pRightInputNerve=NULL;};
	void CutConnectIn( void ) {m_pMainInputNerve=NULL;};

public:
	CNerve* m_pLeftInputNerve;
	CNerve* m_pRightInputNerve;
	CNerve* m_pLeftOutputNerve;
	CNerve* m_pRightOutputNerve;
	CNerve* m_pMainInputNerve;
	CNerve* m_pMainOutputNerve;
	bool m_bLO_to_LI; //true for left out connected to left in, otherwise connected to right in
	bool m_bRO_to_LI; //true for right out connected to left in, otherwise connected to right in
	bool m_bSignalIsMinus;

protected:
	int m_nLeftInputSignal;
	int m_nRightInputSignal;
	int m_nLeftOutputSignal;
	int m_nRightOutputSignal;
	int m_nMainInputSignal;
	int m_nMainOutputSignal;

	int m_nLeftInputSignal_Buf;
	int m_nRightInputSignal_Buf;
	int m_nMainInputSignal_Buf;

	NERVE_ARROW_TYPE m_arrow_type;
	NERVE_POS_TYPE m_pos_type;
	int m_pos_x;
	int m_pos_y;

	NERVE_STATUS m_status;
	int m_countdown;

	int m_potential;
	int m_resting_potential;
	CActiveEPSPList m_ActiveEPSPList;
};

class CNerveArray
{
public:
	CNerveArray(void):
	m_nWidth(0),
	m_nDeep(0),
	m_nInputGroupWidth(0),
	m_nOutputGroupWidth(0),
	m_pNerveArray(NULL),
	m_nRunSteps(0)
	{
	};
	virtual ~CNerveArray(void)
	{
		if(m_pNerveArray)
		{
			for( int i=0; i<m_nWidth*m_nDeep; i++ )
				delete m_pNerveArray[i];
			delete m_pNerveArray;
		}
	};

	void BuildArray( int nWidth, int nDeep, int nInputGroupWidth, int nOutputGroupWidth );

	void ConnectNerves( void );

	virtual CNerve* CreateNerve( NERVE_ARROW_TYPE a_tp, NERVE_POS_TYPE pos_tp, int nPos_x, int nPos_y )
	{return new CNerve( a_tp, pos_tp, nPos_x, nPos_y ); }; 

	void OneStepProcess( int* inputSignals, int* outputSignals ); 

	//void Inspire( void );
	//void Punish( void );

	CNerve** GetNerves( int& nWidth, int& nDeep ) {return m_pNerveArray;};

	CNerve* GetNerve( int pos_x, int pos_y ) { return m_pNerveArray[pos_x+pos_y*m_nDeep]; };

	int Save( unsigned char* pNerveArrayMem );
	bool Load( unsigned char* pNerveArrayMem );

protected:
	int m_nWidth;
	int m_nDeep;
	int m_nInputGroupWidth;
	int m_nOutputGroupWidth;
	int m_nRunSteps;
	CNerve** m_pNerveArray;
};

class CBrain
{
public:
	CBrain(void);
	virtual ~CBrain(void);

	//void Create( int nMsgCount, int nActionCount, SMsgPriority *MsgPriority );
	//void Think( CMsgArray *inputMsgs, CMsgArray *outputMsgs );

	void Inspire( void );
	void Punish( void );

	CBrain* Clone( void );
	CBrain* Breed( CBrain& wife );

	CNerveArray* GetNerveArray( void ) { return m_pNerveArray;};
private:
	int m_nMsgCount;
	int m_nActionCount;

	CNerveArray *m_pNerveArray;
};
